package com.geek.gkpush.framework.threadpool.executor;

import org.apache.dubbo.common.threadpool.support.eager.TaskQueue;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * <h1>基本上是照搬 {@linkplain TaskQueue} 逻辑写的。</h1><br/>
 *
 * <h1><br/><a href="https://u.geekbang.org/subject/arch3/100614201">Java 业务架构实战营：https://u.geekbang.org/subject/arch3/100614201</a></h1><br/><h1><a href="https://gitee.com/ylimhhmily/BizArchPracticeCamp">Java 业务架构实战营代码地址：https://gitee.com/ylimhhmily/BizArchPracticeCamp</a></h1><br/><h1><a href="https://u.geekbang.org/subject/java4th/1001148?source=app_share">高级 Java 工程体系课 2023 版：https://u.geekbang.org/subject/java4th/1001148?source=app_share</a></h1><br/><h1><a href="https://time.geekbang.org/column/intro/100312101?tab=catalog">Dubbo 源码剖析与实战：https://time.geekbang.org/column/intro/100312101?tab=catalog</a></h1>
 *
 * @author hmilyylimh
 *         ^_^
 * @version 0.0.1
 *         ^_^
 * @date 2023/10/2
 *
 */
public class EagerQueue<R extends Runnable> extends LinkedBlockingQueue<Runnable> {

    private static final long serialVersionUID = 4450258867400645828L;

    /** <h2>自定义线程池，改变了任务进入线程池的策略。</h2> **/
    private EagerExecutor executor;

    /**
     * <h2>带参构造方法。</h2>
     *
     * @param capacity
     */
    public EagerQueue(int capacity) {
        super(capacity);
    }

    /**
     * <h2>将队列与线程池关联起来，以便在后续 {@linkplain ThreadPoolExecutor#execute(Runnable)} 方法可以和队列的 offer 方法结合起来改变任务入队策略。</h2>
     *
     * @param exec
     */
    public void setExecutor(EagerExecutor exec) {
        executor = exec;
    }
    @Override
    public boolean offer(Runnable runnable) {
        if (executor == null) {
            throw new RejectedExecutionException("EagerQueue 队列没有绑定线程池对象!");
        }

        // 如果正在干活的线程数量，小于，当前已经开辟的线程数量，则直接把任务丢到队列中，然后就可以马上被 worker 拿到并消费掉
        int currentPoolThreadSize = executor.getPoolSize();
        if (executor.getActiveCount() < currentPoolThreadSize) {
            return super.offer(runnable);
        }

        // 如果还能来到这里，说明线程池中每个 worker 都有活干，但又还小于最大线程数，那么就尝试开辟线程继续处理新来的任务
        if (currentPoolThreadSize < executor.getMaximumPoolSize()) {
            return false;
        }

        // 继续来到这里，则说明最大线程数已经开辟完了，且都在干活了，那么新来的任务就交给队列了，尝试往队列添加，至于是否能添加成功，完全看队列自身了
        return super.offer(runnable);
    }

    /**
     * <h2>重新尝试继续将任务添加至队列，至于能否添加成功，完全看队列自身的容量情况了。</h2>
     *
     * @param task
     * @param timeout
     * @param unit
     * @return
     * @throws InterruptedException
     */
    public boolean retryOffer(Runnable task, long timeout, TimeUnit unit) throws InterruptedException {
        if (executor.isShutdown()) {
            throw new RejectedExecutionException("Executor is shutdown!");
        }
        return super.offer(task, timeout, unit);
    }
}